ComplexHeatmap is probably the most complex package for heatmap visualization.
Its design follows the object-oriented scheme, so heatmap body, annotations, legends etc. are self-contained building blocks. It can do a lot and it can be a bit overhelming, but for simple heatmaps (e.g. z-score of logcounts), unlike heatmap.2 and similar packages, ComplexHeatmap is more user-friendly and provides you a great assistance.
A single heatmap composition.
The basic classes are:
Heatmap: a single heatmap containing heatmap body, row/column names, titles, dendrograms and row/column annotations.HeatmapList: a list of heatmaps and heatmap annotations.HeatmapAnnotation: defines a list of row annotations and column annotations. The heatmap annotations can be components of heatmap, also they can be independent as heatmaps.AnnotationFunction: a single annotation created by anno_*() functions (e.g. anno_barplot()).ComplexHeatmap offers a wide variety of annotations. Here are some examples of its capabilities:
OncoPrint: visualize multiple genomic alteration events.
UpSet plots: an efficient way to visualize intersections of multiple sets compared to the traditional approaches, i.e. the Venn Diagram.
UpSet plot
ComplexHeatmap was primarily developed for omics data, for example to show correlations in expression and methylation data. For that cases it offers the HeatmapList() class.
Multiple heatmaps (rowwise).
Multiple heatmaps (colwise).
library(ComplexHeatmap)
Let’s prepare a 10x10 matrix, sample sheet and color palette. It’s really weird, but if you let ComplexHeatmap to generate annotation colors automatically, it assigns them randomly, so rather use your own color mapping.
m <- matrix(
c(
# Sex: female
rnorm(10, mean = 5),
rnorm(10, mean = 4),
# Sex: male
rnorm(20, mean = 1),
rnorm(20, mean = 2),
rnorm(20, mean = 3),
# Sex: unknown
rnorm(10, mean = 10),
rnorm(10, mean = 15)
),
nrow = 10,
ncol = 10
)
rownames(m) <- paste0("gene_", 1:nrow(m))
colnames(m) <- paste0("sample_", 1:ncol(m))
m_scaled <- t(scale(t(m)))
m_scaled
## sample_1 sample_2 sample_3 sample_4 sample_5 sample_6 sample_7 sample_8 sample_9 sample_10
## gene_1 0.12196160 -0.23647370 -0.7810816 -0.5093973 -0.6776056 -0.5794762 -0.44735760 -0.47193894 1.3276885 2.253681
## gene_2 0.09074330 -0.52080499 -0.5996908 -0.4601156 -0.5782427 -0.4886422 -0.64776144 -0.36520413 1.2140520 2.355667
## gene_3 0.05488935 -0.19397342 -0.6656398 -0.7321454 -0.5696365 -0.5445640 -0.59684004 -0.23836426 1.0839240 2.402350
## gene_4 0.02733499 0.34650549 -1.0674187 -0.8198248 -0.3244079 -0.6466206 -0.26088264 -0.66138713 1.3598152 2.046886
## gene_5 0.15875700 -0.10596029 -0.9981675 -0.6741977 -0.4351860 -0.3677744 -0.31758716 -0.66624627 1.0544728 2.351890
## gene_6 0.37424999 -0.21689459 -0.6430358 -0.7877546 -0.7881338 -0.7614095 -0.36896021 -0.14134724 1.0084508 2.324835
## gene_7 -0.04228254 -0.26668832 -0.5979087 -0.8081833 -0.5596218 -0.5696198 -0.09747231 -0.64115122 1.3226736 2.260254
## gene_8 -0.02874579 -0.03837231 -0.6614387 -1.0017819 -0.7480161 -0.4670512 -0.49751446 0.02166648 1.1281800 2.293074
## gene_9 -0.02120559 -0.00666493 -0.7352408 -0.9965580 -0.6292621 -0.6421625 0.17262755 -0.61811192 1.4202523 2.056326
## gene_10 0.32765371 -0.05392921 -0.6717025 -0.9924981 -0.7173262 -0.8607725 0.14238221 -0.28784720 0.7676337 2.346406
## attr(,"scaled:center")
## gene_1 gene_2 gene_3 gene_4 gene_5 gene_6 gene_7 gene_8 gene_9 gene_10
## 5.223362 4.479634 3.941294 4.892279 4.658802 4.371019 4.798317 4.720111 4.619748 4.376017
## attr(,"scaled:scale")
## gene_1 gene_2 gene_3 gene_4 gene_5 gene_6 gene_7 gene_8 gene_9 gene_10
## 4.923097 4.156381 4.228509 4.597883 5.261948 4.344841 4.898211 3.962298 4.223261 5.145020
sample_sheet <- data.frame(
Sex = c(rep("female", 2), rep("male", 6), rep("unknown", 2)),
Treatment = c(rep("control", 4), rep("alcohol", 4), rep("coffeine", 2)),
stringsAsFactors = TRUE
)
sample_sheet
# This is a default ggplot2 palette.
color_palette <- scales::hue_pal()(6)
color_palette
## [1] "#F8766D" "#B79F00" "#00BA38" "#00BFC4" "#619CFF" "#F564E3"
scales::show_col(color_palette)
Color mapping is similar to ggplot2::scale_color_manual(values = c(...)). It’s a list where values are named vectors of factor levels and names of the list values correspond to names of annotations.
color_mapping <- list(Sex = color_palette[1:3], Treatment = color_palette[4:6])
names(color_mapping$Sex) <- levels(sample_sheet$Sex)
names(color_mapping$Treatment) <- levels(sample_sheet$Treatment)
color_mapping
## $Sex
## female male unknown
## "#F8766D" "#B79F00" "#00BA38"
##
## $Treatment
## alcohol coffeine control
## "#00BFC4" "#619CFF" "#F564E3"
p_heatmap_1 <- Heatmap(
m_scaled,
name = "z-score",
show_row_names = TRUE,
column_title = "Main column title",
top_annotation = HeatmapAnnotation(Sex = sample_sheet$Sex, Treatment = sample_sheet$Treatment, col = color_mapping),
heatmap_legend_param = list(legend_direction = "vertical")
)
draw(p_heatmap_1, merge_legend = TRUE)
By default, heatmap color legend is separated from annotation legends (it lies in separate column). So to save space it is better to merge them, and for that you need to call the draw() method with merge_legend = TRUE.
Alternatively you can split columns by defined groups and apply clustering within them:
p_heatmap_2 <- Heatmap(
m_scaled,
name = "z-score",
cluster_column_slices = FALSE,
show_row_names = TRUE,
column_title = "Main column title",
column_split = sample_sheet$Sex,
top_annotation = HeatmapAnnotation(Sex = sample_sheet$Sex, Treatment = sample_sheet$Treatment, col = color_mapping),
heatmap_legend_param = list(legend_direction = "vertical")
)
draw(p_heatmap_2, merge_legend = TRUE)
p_heatmap_3 <- Heatmap(
m_scaled,
name = "z-score",
cluster_column_slices = FALSE,
show_row_names = TRUE,
column_title = "Main column title",
column_split = sample_sheet$Sex,
top_annotation = HeatmapAnnotation(Sex = sample_sheet$Sex, Treatment = sample_sheet$Treatment, col = color_mapping),
left_annotation = rowAnnotation(density = anno_density(m_scaled, joyplot_scale = 2, gp = gpar(fill = 1:10))),
right_annotation = rowAnnotation(boxplots = anno_boxplot(m_scaled, width = unit(4, "cm"), gp = gpar(fill = 1:10))),
heatmap_legend_param = list(legend_direction = "vertical")
)
draw(p_heatmap_3, merge_legend = TRUE)
You can see that this heatmap is similar to the second one (p_heatmap_2). Almost everything you pass in parameters of Heatmap() constructor can be also modified in the existing heatmap object:
p_heatmap_3@left_annotation <- rowAnnotation(density = anno_density(m_scaled, joyplot_scale = 2, gp = gpar(fill = 1:10)))
p_heatmap_3@right_annotation <- rowAnnotation(boxplots = anno_boxplot(m_scaled, width = unit(4, "cm"), gp = gpar(fill = 1:10)))